// SearchView.cpp : implementation file
//

#include "stdafx.h"
#include "Finance.h"

#include "CDataFile.h"

#include "FinanceDoc.h"
#include "FinanceView.h"
#include "CountView.h"

#include "PageDateRange.h"
#include "DlgAddNew.h"
#include "DlgModify.h"
#include "DlgSearch.h"

#include "SearchView.h"


#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif


extern CFinanceApp theApp;       

/////////////////////////////////////////////////////////////////////////////
// CSearchView

IMPLEMENT_DYNCREATE(CSearchView, CListEditView)

//
//------------------------------------------------------------
CSearchView::CSearchView()
{
	m_ordercolumn = date;
	m_disporder	  = desc;
}

//
//------------------------------------------------------------
CSearchView::~CSearchView()
{
	if( m_pRecordset != NULL )
	{
		if( m_pRecordset->State )
			m_pRecordset->Close();
		m_pRecordset = NULL;
	}
}


BEGIN_MESSAGE_MAP(CSearchView, CListEditView)
	//{{AFX_MSG_MAP(CSearchView)
	ON_NOTIFY_REFLECT(NM_DBLCLK, OnDblclkList)
	ON_NOTIFY_REFLECT(LVN_COLUMNCLICK, OnColumnclick)
	ON_WM_CREATE()
	ON_COMMAND(ID_SEARCH, OnSearch)
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CSearchView drawing

void CSearchView::OnDraw(CDC* pDC)
{
	CDocument* pDoc = GetDocument();
	// TODO: add draw code here
}

/////////////////////////////////////////////////////////////////////////////
// CSearchView diagnostics

#ifdef _DEBUG
void CSearchView::AssertValid() const
{
	CListView::AssertValid();
}

void CSearchView::Dump(CDumpContext& dc) const
{
	CListView::Dump(dc);
}
#endif //_DEBUG

/////////////////////////////////////////////////////////////////////////////
// CSearchView message handlers

//
//------------------------------------------------------------
void CSearchView::OnInitialUpdate() 
{
	CListEditView::OnInitialUpdate();
	
	// Gain a reference to the list control itself
	CListCtrl&		InnerLst = GetListCtrl();
	HDITEM			hditem;
	CFont			m_font;
	INT				i, j;
	
	
	m_imagelist.Create(IDB_HEADER, 9, 1, RGB(255,255,255));
	m_bitmap.LoadBitmap(IDB_BITMAP);
	
	CFlatHeaderCtrl* pFlatHeader = (CFlatHeaderCtrl*)InnerLst.GetHeaderCtrl();
	pFlatHeader->ModifyProperty(FH_PROPERTY_STATICBORDER, TRUE);
	pFlatHeader->SetImageList(&m_imagelist);
	
	
	for( j=0,i=0; i<ACCOUNT_COLUMNS; i++ )
	{
		InnerLst.InsertColumn( j,	g_myColumn[i].chName, 
			g_myColumn[i].iFormat, 
			g_myColumn[i].iWidth );
		if( 1 == g_myColumn[i].btImgFlag ) 
		{
			hditem.mask = HDI_FORMAT|HDI_IMAGE;	//|HDI_BITMAP;	
			pFlatHeader->GetItem( j, &hditem );
			hditem.fmt |= HDF_IMAGE;			//|HDF_BITMAP;
			hditem.iImage = g_myColumn[i].btmgIdx;
			hditem.hbm = (HBITMAP)m_bitmap;
			pFlatHeader->SetItem( j, &hditem );
		}
		j++;
	}
	
	m_font.CreateFont(16, 0,0,0,FW_NORMAL, 0,0,0,
		DEFAULT_CHARSET, OUT_CHARACTER_PRECIS, CLIP_CHARACTER_PRECIS,
		DEFAULT_QUALITY, DEFAULT_PITCH | FF_DONTCARE, "Arial");
	InnerLst.SetFont(&m_font);
	
	// <> Init list ctrl
	InnerLst.SetExtendedStyle(LVS_EX_GRIDLINES|LVS_EX_HEADERDRAGDROP|LVS_EX_FULLROWSELECT);
	InnerLst.SetBkColor(RGB(241,253,254)); //(RGB(241,253,254)),RGB(224,242,248)
	//InnerLst.SetTextColor(RGB(0,0,255));
	InnerLst.SetTextBkColor(RGB(241,253,254));
	
	HDITEMEX hditemex;
	hditemex.iMinWidth = 21;
	hditemex.iMaxWidth = 200;
	pFlatHeader->SetItemEx(1, &hditemex);
	
	// <> _CommandPtr init
	m_pCommand.CreateInstance(__uuidof(Command));
	m_pCommand->ActiveConnection = theApp.m_pConnection.GetInterfacePtr();

	// <> Open RecordSet
	m_pRecordset.CreateInstance(__uuidof(Recordset));
	
	// <> Refresh
	OnRefresh();
}

//
//------------------------------------------------------------
BOOL CSearchView::PreCreateWindow(CREATESTRUCT& cs) 
{
	cs.style |= LVS_REPORT |
				LVS_SINGLESEL |
				LVS_SHOWSELALWAYS; 
				//LVS_OWNERDRAWFIXED;
	
	return CListView::PreCreateWindow(cs);
}

//
//------------------------------------------------------------
int CSearchView::OnCreate(LPCREATESTRUCT lpCreateStruct) 
{
	if (CListEditView::OnCreate(lpCreateStruct) == -1)
		return -1;
	
	ASSERT( GetStyle() & LVS_REPORT );
	
	// Gain a reference to the list control itself
	CListCtrl&		InnerLst = GetListCtrl();
	CHeaderCtrl*	pHeadCtrl = InnerLst.GetHeaderCtrl();
	
	ASSERT( pHeadCtrl );
	
	VERIFY( m_FlatHeader.SubclassWindow( pHeadCtrl->GetSafeHwnd() ) );
	
	return 0;
}

//
// For Other class invoke
//------------------------------------------------------------
void CSearchView::RefreshSearchView()
{
	OnRefresh();
}

//
//------------------------------------------------------------
void CSearchView::OnSearch() 
{
	CDlgSearch		dlg;
	
	
	if( dlg.DoModal() == IDOK ) 
	{
		OnRefresh();
	}
}

//
//------------------------------------------------------------
void CSearchView::OnRefresh()
{
	// <> open Account by condition
	OpenAccount();
	
	// <> Fill listctrl content
	ShowAccountRec( -1 );
}

// 
// order is ^ after first click
// ^ is a->z, 2005/01/01 -> 2005/07/01
// pNMListView->iSubItem will not be changed postion after user drag head column
//-------------------------------------------------------------------------------
void CSearchView::OnColumnclick(NMHDR* pNMHDR, LRESULT* pResult) 
{
	NM_LISTVIEW*	pNMListView = (NM_LISTVIEW*)pNMHDR;
	BOOL			bSortAscending;
	// map List column order to ORDERCOLUMN define
	ORDERCOLUMN		iSQLOrder[ACCOUNT_COLUMNS] ={	// listctrl  
								date,			// 0
								amount,			// 1
								purpose,		// 2
								flowclass,		// 3
								owner,			// 4
								comment			// 5
						};
	
	
	// If clicked on already sorted column, reverse sort order
	if(pNMListView->iSubItem == m_FlatHeader.GetSortColumn(&bSortAscending))
		bSortAscending = !bSortAscending;
	else
        bSortAscending = TRUE;
	
	m_FlatHeader.SetSortColumn(pNMListView->iSubItem, bSortAscending);
	
	// Do some sort of sorting...
	m_ordercolumn = iSQLOrder[ pNMListView->iSubItem ];
	m_disporder = ( bSortAscending ) ? asc : desc;
	
	// <> Refresh
	OnRefresh();
	
	// Note!! m_iLstCurSel will be -1 after ShowAccountRec(), 
	// even if m_iLstCurSel be assigned a valid value before.
	
	*pResult = 0;
}


// 
// For modify
// Note! must be sync listview and count view
//--------------------------------------------------------------
void CSearchView::OnDblclkList(NMHDR* pNMHDR, LRESULT* pResult) 
{
	CListCtrl&		InnerLst = GetListCtrl();
	POSITION		pos = InnerLst.GetFirstSelectedItemPosition();
	INT				iLstCurSel = InnerLst.GetNextSelectedItem(pos);  // Get item index
	

	if( iLstCurSel >= 0 )
	{
		CDlgModify	dlg;
		DWORD		lAbsidx = 0;
		
	
		lAbsidx = InnerLst.GetItemData( iLstCurSel );
		if( lAbsidx > 0 ) 
		{
			dlg.SetSelIndex( lAbsidx );
			
			if( dlg.DoModal() == IDOK )	
			{
				// <> refresh Search view
				OnRefresh(); 

				// <> refresh List view
				CFinanceView	*pLstView = NULL;
				CFinanceDoc		*pDoc=(CFinanceDoc*)GetDocument();
				
				if( pDoc )
					pLstView=(CFinanceView*)pDoc->GetView(RUNTIME_CLASS(CFinanceView));
				
				if( pLstView )
					pLstView->RefreshListView();

				// Will refresh Count view in CCountView::OnActivateView()
			}
		}
	}
	*pResult = 0;
}


//
// open Inpatient by SQL segment from ini 
//------------------------------------------------------------
void CSearchView::OpenAccount()
{
	CDataFile	IniDF( MONEY_CFG );
	_variant_t	var;
	CHAR		chSQL[INI_MAX_BUFLEN+100] = { 0 };
	t_Str		sdStrTmp;
	CString		csSQL;
	CHAR		*chOrder[] = { "ASC", "DESC" };
	CHAR		*chColumn[] = {	// the array order is same as ORDERCOLUMN define
								"Amount ",
								"Purpose ",
								"FlowClass ",
								"Date ",			// date
								"Owner ",
								"Comment "
							};
	
	
	if( m_pRecordset->State )
		m_pRecordset->Close();
	
	// <> compose SQL segment
	csSQL = "SELECT * FROM Account WHERE ";
	
	// <> get SQL segment
	sdStrTmp = IniDF.GetValue( "SQL", "SQL segment" );
	if( 0 == sdStrTmp.length() )
	{
		CountSearchInfo( chSQL );
		return ;
	}

	sdStrTmp.copy( chSQL, sdStrTmp.length(), 0 );

	// Count info
	CountSearchInfo( chSQL );

	// compose SQL 
	csSQL += chSQL;
	csSQL += " ORDER BY ";
	csSQL += chColumn[ m_ordercolumn ];
	csSQL += chOrder[ m_disporder ];
	var = csSQL;
	try
	{
		m_pRecordset->Open( var,   
			theApp.m_pConnection.GetInterfacePtr(),	 // Get the Ptr of connect lib
			adOpenStatic,
			adLockOptimistic,
			adCmdText);
	}
	catch(_com_error *e)
	{
		AfxMessageBox(e->ErrorMessage());
	}
}

//
//------------------------------------------------------------
void CSearchView::ShowAccountRec(LONG lAbsIdx)
{
	INT				i, j, iItem;
	_variant_t		var;
	CString			csItem[2];
	CListCtrl&		InnerLst = GetListCtrl();
	// fields name of account table
	CHAR			*chAccount[] = {
									"Date",
									"Amount",
									"Purpose",
									"FlowClass",
									"Owner",
									"Comment"									
									};
		
	
	//<> reset state
	InnerLst.DeleteAllItems();
	
	// <> No SQL seg in ini file, only change columns
	if( !m_pRecordset->State )
		return ;
	
	try
	{
		if(!m_pRecordset->BOF)
			m_pRecordset->MoveFirst();
		else
		{
			//AfxMessageBox("Search table is NULL");
			return;
		}
		
		iItem = 0;
		while(!m_pRecordset->adoEOF)
		{
			var = m_pRecordset->GetCollect( chAccount[0] );	// date
			if(var.vt != VT_NULL)
				csItem[0] = (LPCSTR)_bstr_t(var);
			else
				csItem[0].Empty();	
			
			InnerLst.InsertItem( iItem, csItem[0], 0 );	
			
			for( j=1,i=1; i<ACCOUNT_COLUMNS; i++ )
			{
				var = m_pRecordset->GetCollect( chAccount[i] );	
				if(var.vt != VT_NULL)
					csItem[1] = (LPCSTR)_bstr_t(var);
				else
					csItem[1].Empty();	// comment might be NULL in Table
				
				InnerLst.SetItemText( iItem, j++, csItem[1] );	
			}

			var = m_pRecordset->GetCollect("Index");	// LONG
			if(var.vt != VT_NULL)
				InnerLst.SetItemData( iItem, var.lVal );
			
			
			m_pRecordset->MoveNext();
			iItem++;
		}
		
	}
	catch(_com_error *e)
	{
		AfxMessageBox(e->ErrorMessage());
	}
	
}

// 
// 
//------------------------------------------------------------------------------------------
void CSearchView::CountSearchInfo( LPTSTR chSQLseg )
{
	_RecordsetPtr	pRecSet;
	_variant_t		var;
	LONG			lCount = 0;
	DOUBLE			dbSum = 0;
	DOUBLE			dbAvg = 0;
	CHAR			chSQL[INI_MAX_BUFLEN+100] = { 0 };
	

	if( chSQLseg[0] != 0 ) 
	{		
		// <> Compose SQL 
		strcpy( chSQL, "SELECT COUNT(*),SUM(Amount),AVG(Amount) FROM Account WHERE " );
		strcat( chSQL, chSQLseg );
		try
		{
			m_pCommand->CommandText = chSQL;
			pRecSet = m_pCommand->Execute( NULL, NULL, adCmdText );
			
			if( !pRecSet->adoEOF )
			{
				var = pRecSet->GetCollect( (_variant_t)(long)0 );
				lCount = var.lVal;	// long
				
				var = pRecSet->GetCollect( (_variant_t)(long)1 );
				switch( var.vt ) 
				{
				case VT_I4:	// long
					dbSum = var.lVal;
					break ;
					
				case VT_R4: // float
					dbSum = var.fltVal;
					break ;
					
				case VT_R8: // double
					dbSum = var.dblVal;
					break ;
					
				default :
					dbSum = 0;
					break ;
				}
				
				var = pRecSet->GetCollect( (_variant_t)(long)2 );
				switch( var.vt ) 
				{
				case VT_I4:	// long
					dbAvg = var.lVal;
					break ;
					
				case VT_R4: // float
					dbAvg = var.fltVal;
					break ;
					
				case VT_R8: // double
					dbAvg = var.dblVal;
					break ;
					
				default :
					dbAvg = 0;
					break ;
				}
			}

			pRecSet->Close();
		}
		catch(_com_error *e)
		{
			AfxMessageBox(e->ErrorMessage());
		}
	}

	//	  Get pointer to status bar .
	CStatusBar *pStatusBar = (CStatusBar*)AfxGetMainWnd()->GetDescendantWindow(AFX_IDW_STATUS_BAR);
	sprintf( chSQL, " Search : %ld, Total : %.2f, Average : %.2f ", lCount, dbSum, dbAvg );
	pStatusBar->SetPaneText( 3, chSQL );
	
}

// 
// 
//------------------------------------------------------------------------------------------

